#include <gtest/gtest.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <atomic>

#include "file_write.h"
#include "log_service.h"
#include "data_buffer.h"

class FileWriteTest : public ::testing::Test {
protected:
    /**
     * @brief Initializes the file writing module for testing.
     * @details Called before each test case to set up the file writing environment.
     * @return void
     */
    void SetUp() override
    {
        // Remove test generated files
        int ret = system("rm -f run-*.log");
        (void) ret;
        // Initialize file writing module
        ASSERT_EQ(file_write_init(), 0);
    }

    void TearDown() override
    {
        // Clean up file writing module
        file_write_deinit();

        // Remove test generated files
        int ret = system("rm -f run-*.log");
        (void) ret;
    }
};

// Test basic file writing functionality
TEST_F(FileWriteTest, BasicWrite)
{
    const char *test_data = "Hello, world!";
    size_t data_len = strlen(test_data);

    // Write test data
    EXPECT_EQ(file_write(test_data, data_len), 0);

    // Flush buffer
    EXPECT_EQ(file_write_flush(), 0);

    // Check file existence
    FILE *pp = popen("ls run-*.log | wc -l", "r");
    char result[20];
    EXPECT_NE(fgets(result, sizeof(result), pp), nullptr);
    pclose(pp);
    int file_count = atoi(result);
    EXPECT_EQ(file_count, 1);
}

// Test large file writing
TEST_F(FileWriteTest, LargeFileWrite)
{
    // Prepare large test data (1MB)
    const size_t large_size = 1024 * 1024;
    char *large_data = new char[large_size];
    memset(large_data, 'A', large_size);

    // Write large data block
    EXPECT_EQ(file_write(large_data, large_size), 0);

    // Flush buffer
    EXPECT_EQ(file_write_flush(), 0);

    delete[] large_data;
}

// Test buffer boundary conditions
TEST_F(FileWriteTest, BufferBoundary)
{
    // Write data that exactly fills buffer
    const size_t buf_size = WRITE_BUFFER_SIZE;
    char *buf_data = new char[buf_size];
    memset(buf_data, 'B', buf_size);

    EXPECT_EQ(file_write(buf_data, buf_size), 0);

    // Write 1 more byte to trigger flush
    EXPECT_EQ(file_write("X", 1), 0);

    delete[] buf_data;
}

// Test file write error handling
TEST_F(FileWriteTest, ErrorHandling)
{
    // Attempt to write null pointer
    EXPECT_EQ(file_write(nullptr, 10), -1);

    // Attempt to write 0 bytes
    EXPECT_EQ(file_write("test", 0), 0);
}

// Test file auto rotation
TEST_F(FileWriteTest, FileRotation)
{
    char *over_data = new char[WRITE_BUFFER_SIZE];
    memset(over_data, 'C', WRITE_BUFFER_SIZE);

    // Write data exceeding max file size
    for (int i = 0; i < MAX_LOG_FILE_SIZE / WRITE_BUFFER_SIZE; i ++) {
        EXPECT_EQ(file_write(over_data, WRITE_BUFFER_SIZE), 0);
    }
    // wait 2 seconds
    sleep(2);
    EXPECT_EQ(file_write(over_data, WRITE_BUFFER_SIZE), 0);
    // Should auto create new file
    EXPECT_EQ(file_write_flush(), 0);

    // Check for multiple log files
    FILE *pp = popen("ls run-*.log | wc -l", "r");
    char result[20];
    EXPECT_NE(fgets(result, sizeof(result), pp), nullptr);
    pclose(pp);
    int file_count = atoi(result);
    EXPECT_EQ(file_count, 2);

    delete[] over_data;
}

// Test buffer pool basic functionality
TEST(DataBufferTest, BufferPoolBasic)
{
    init_buffer_pool();
    init_message_queue();

    // Get buffer
    data_buffer_t *buf = get_buffer();
    ASSERT_NE(buf, nullptr);
    // Return buffer
    return_buffer(buf);

    deinit_message_queue();
    deinit_buffer_pool();
}

// Test buffer pool exhaustion
TEST(DataBufferTest, BufferPoolExhaust)
{
    init_buffer_pool();
    init_message_queue();

    data_buffer_t *buffers[BUFFER_POOL_SIZE];

    // Acquire all buffers
    for (int i = 0; i < BUFFER_POOL_SIZE; i++) {
        buffers[i] = get_buffer();
        ASSERT_NE(buffers[i], nullptr);
    }

    // Should fail when acquiring extra buffer
    ASSERT_EQ(get_buffer(), nullptr);

    // Return all buffers
    for (int i = 0; i < BUFFER_POOL_SIZE; i++) {
        return_buffer(buffers[i]);
    }
    deinit_message_queue();
    deinit_buffer_pool();
}

// Test message queue basic functionality
TEST(DataBufferTest, MessageQueueBasic)
{
    init_buffer_pool();
    init_message_queue();
    data_buffer_t *buf = get_buffer();
    ASSERT_NE(buf, nullptr);

    // Fill test data
    strncpy(buf->data, "test message", MAX_MSG_SIZE);
    buf->length = strlen("test message");

    // Enqueue message
    enqueue_message(buf);

    // Dequeue message
    data_buffer_t *dequeued = dequeue_message_timed(100);
    ASSERT_NE(dequeued, nullptr);
    ASSERT_EQ(dequeued, buf);
    ASSERT_STREQ(dequeued->data, "test message");

    return_buffer(dequeued);
    deinit_message_queue();
    deinit_buffer_pool();
}

// Test message queue timeout
TEST(DataBufferTest, MessageQueueTimeout)
{
    init_buffer_pool();
    init_message_queue();

    // Should timeout and return null when dequeuing from empty queue
    data_buffer_t *dequeued = dequeue_message_timed(100);
    ASSERT_EQ(dequeued, nullptr);
    deinit_message_queue();
    deinit_buffer_pool();
}
